package org.wheel.plugins.toolkit.sql.core;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.Setter;
import org.wheel.plugins.toolkit.sql.capability.*;
import org.wheel.plugins.toolkit.sql.driver.JsqlParserDriver;
import org.wheel.plugins.toolkit.sql.entity.BeanMeta;
import org.wheel.plugins.toolkit.sql.enums.CompareType;
import org.wheel.plugins.toolkit.sql.enums.Dialect;
import org.wheel.plugins.toolkit.sql.enums.LogicalOp;
import org.wheel.plugins.toolkit.sql.enums.SqlModel;
import org.wheel.plugins.toolkit.sql.helper.ColumnRef;
import org.wheel.plugins.toolkit.sql.helper.ParamBank;
import org.wheel.plugins.toolkit.sql.helper.SFunction;
import org.wheel.plugins.toolkit.sql.render.*;
import org.wheel.plugins.toolkit.sql.segment.*;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 抽象Wrapper
 * @param <T> 实体类
 * @param <W> Wrapper实现类
 */
public abstract class AbstractWrapper<T, W extends AbstractWrapper<T, W>>
        implements Wrapper<T>
        , LambdaJoin<W, T>
        , LambdaCompare<W, T>
        , LambdaColumnCompare<W, T>
        , LambdaSemiCompare<W, T>
        , LambdaScalar<W, T>
        , LambdaOr<W, T>
        , SqlCompare<W, T> {

    protected final SegmentTree tree;
    private static final String defaultTableAlias = "t";
    @Getter
    protected String mainTableAlias = defaultTableAlias;
    protected final Class<T> clazz;
    @Getter
    @Setter
    private WrapperContext context;
    // 本地方言
    @Getter
    protected Dialect dialect;   // ① 默认值
    // 输出SQL的默认模式
    @Getter
    protected SqlModel sqlModel;

    /**
     * 为外部提供 参数对象
     * 只有在SQL生成后，参数才能获取
     */
    private LinkedHashMap<String, Object> paramMap;

    public AbstractWrapper(Class<T> clazz) {
        this(clazz, defaultTableAlias);
    }

    public AbstractWrapper(Class<T> clazz, String tableAlias) {
        this(clazz, tableAlias, new WrapperContext());
    }

    public AbstractWrapper(Class<T> clazz, String tableAlias, WrapperContext context){
        this.dialect = GlobalConfig.getDialect();   // 全局可覆盖
        this.sqlModel = GlobalConfig.getSqlModel();  // 默认走全局
        this.clazz = clazz;
        this.mainTableAlias = tableAlias;
        this.tree = new SegmentTree(BeanMeta.of(clazz).getFullTableName()).as(mainTableAlias);
        this.context = context;
        this.context.getAliasRegistry().register(clazz, mainTableAlias);
    }

    /**
     * 创建子查询 Wrapper，返回具体子类类型，保证链式可用
     */
    @SuppressWarnings("unchecked")
    public <E, W2 extends AbstractWrapper<E, W2>> W2 newSubWrapper(Class<E> clazz, String alias) {
        try {
            Class<? extends AbstractWrapper<?, ?>> impl =
                    (Class<? extends AbstractWrapper<?, ?>>) this.getClass();
            Constructor<?> c = impl.getDeclaredConstructor(
                    Class.class, String.class, WrapperContext.class);
            return (W2) c.newInstance(clazz, alias, this.getContext());
        } catch (ReflectiveOperationException e) {
            throw new IllegalStateException(
                    "子类必须提供 public XXX(Class,String,WrapperContext) 构造器", e);
        }
    }

    @SuppressWarnings("unchecked")
    protected W self() {
        return (W) this;
    }

    /* 新增链式方法 */
    @Override
    public W dialect(Dialect dialect) {
        this.dialect = dialect;
        return self();
    }
    /* 新增链式方法 */
    @Override
    public W sqlModel(SqlModel model) {
        GlobalConfig.setSqlModel(model);
        this.sqlModel = GlobalConfig.getSqlModel();
        return self();
    }

    @Override
    public W as(String alias) {
        this.context.getAliasRegistry().replaceAlias(clazz, alias);
        this.mainTableAlias = alias;
        tree.as(alias);
        return self();
    }

    @Override
    public String getMainAlias() {
        return mainTableAlias;
    }

    @Override
    public Class<T> getEntityClass() {
        return this.clazz;
    }

    public String resolveAlias(Class<?> entityClass, int joinIndex) {
        return this.context.getAliasRegistry().resolveAlias(entityClass, joinIndex);
    }

    @Override
    public <J> W join(JoinType joinType, Class<J> joinClazz, String tableAlias, Consumer<OnClause<J>> onConsumer) {
        if(StrUtil.isBlank(tableAlias)){
            tableAlias = this.context.getAliasRegistry().nextAlias();
        }
        this.context.getAliasRegistry().register(joinClazz, tableAlias);
        OnClause<J> on = new OnClause<>(tableAlias, self());
        onConsumer.accept(on);

        tree.join(joinType, BeanMeta.of(joinClazz).getFullTableName(), tableAlias, on);
        return self();
    }

    @Override
    public <J> W compare(boolean condition, CompareType type, SFunction<J, ?> column, Object val) {
        if(condition) {
            tree.where(WhereSegment.of(type, ColumnRef.of(column, self()).withoutAlias(), val, this));
        }
        return self();
    }

    @Override
    public <J> W compareList(boolean condition, CompareType type, SFunction<J, ?> col, Collection<?> val) {
        if(condition) {
            tree.where(WhereSegment.of(type, ColumnRef.of(col, self()).withoutAlias(), val, this));
        }
        return self();
    }

    @Override
    public W compare(boolean condition, CompareType type, String col, Object val) {
        if(condition) {
            tree.where(WhereSegment.of(type, ColumnRef.of(null, col,  null)
                    .withWrapper(this).withoutAlias(), val, this));
        }
        return self();
    }

    @Override
    public <R> W compareList(boolean condition, CompareType type, String col, Collection<?> val) {
        if(condition) {
            tree.where(WhereSegment.of(type, ColumnRef.of(null, col,  null)
                    .withWrapper(this).withoutAlias(), val, this));
        }
        return self();
    }

    @Override
    public <J> W compareCol(CompareType type, String leftTableAlias, SFunction<T, ?> leftCol, String rightTableAlias, SFunction<J, ?> rightCol) {
        String l = null;
        if(StrUtil.isBlank(leftTableAlias)){
            l = ColumnRef.of(leftCol,this).withoutAlias().getSql();
        }else{
            l = ColumnRef.of(leftCol, leftTableAlias).withoutAlias().getSql();
        }
        String r = ColumnRef.of(rightCol, rightTableAlias).withoutAlias().getSql();
        WhereSegment seg = WhereSegment.of(LogicalOp.AND, l + " " + type.getOp() + " " + r, this);
        tree.where(seg);
        return self();
    }



    /* ----- 列 vs 原生 SQL ----- */
    @Override
    public W compareSql(CompareType type, SFunction<T, ?> leftCol, String rawSqlRight, AbstractWrapper<T, ?> parentWrapper) {
        String l = ColumnRef.of(leftCol, this).getSql();
        WhereSegment seg = WhereSegment.of(LogicalOp.AND, l + " " + type.getOp() + " " + rawSqlRight, this);
        tree.where(seg);
        return self();
    }

    /* ----- 列 vs 子查询 ----- */
    @Override
    public <E> W inSub(boolean condition, SFunction<T, ?> leftCol, AbstractWrapper<E, ?> subQuery, AbstractWrapper<T, ?> parentWrapper) {
        if(condition) {
            // 不提前 getSql()，只包装成延时片段
            SubQuerySegment subQuerySegment = new SubQuerySegment(subQuery, parentWrapper);
            // ② 使用重编号后的 SQL
            String l = ColumnRef.of(leftCol, this).withoutAlias().getSql();
            WhereSegment seg = WhereSegment.of(LogicalOp.AND,
                    l + " " + CompareType.IN.getOp() + " (" + subQuerySegment.getSql() + ")", this);
            tree.where(seg);
        }
        return self();
    }


    @Override
    public <E> W notInSub(boolean condition, SFunction<T,?> leftCol, AbstractWrapper<E, ?> subQuery, AbstractWrapper<T, ?> parentWrapper) {
        if(condition) {
            // 不提前 getSql()，只包装成延时片段
            SubQuerySegment subQuerySegment = new SubQuerySegment(subQuery, parentWrapper);
            // ② 使用重编号后的 SQL
            String l = ColumnRef.of(leftCol, this).withoutAlias().getSql();
            WhereSegment seg = WhereSegment.of(LogicalOp.AND, l + " " + CompareType.NOT_IN.getOp() + " (" + subQuerySegment.getSql() + ")", this);
            tree.where(seg);
        }
        return self();
    }

    @Override
    public <E>  W exists(boolean condition, boolean not, AbstractWrapper<E, ?> subQuery, AbstractWrapper<T, ?> parentWrapper) {
        if(condition){
            // 不提前 getSql()，只包装成延时片段
            SubQuerySegment subQuerySegment = new SubQuerySegment(subQuery, parentWrapper);
            // ② 使用重编号后的 SQL
            WhereSegment seg = WhereSegment.of(LogicalOp.AND, not ? "NOT EXISTS" : "EXISTS"+" ("+subQuerySegment.getSql()+")", this);
            tree.where(seg);
        }
        return self();
    }

    @Override
    public W or() {
        tree.where(WhereSegment.of(LogicalOp.OR, "", this));
        return self();
    }

    @Override
    public W or(boolean condition, SFunction<W, W> nested) {
        if (!condition || nested == null){
            return self();
        }
        int initIndex = tree.getWhereSegments().size();

        nested.apply(self());
        int currentIndex = tree.getWhereSegments().size();
        if (currentIndex == initIndex) {
            return self();   // 空段直接返回
        }
        if(!tree.getWhereSegments().isEmpty()) {
            // 2. 倒序遍历，修正连接符 中间的逻辑运算符，默认为 AND，需要看看前面有没有 空值
            for (int i = currentIndex - 1; i >= initIndex; i--) {
                AbstractWhereSegment seg = tree.getWhereSegments().get(i);
                // ① 看看是否有前一个对象
                AbstractWhereSegment prevSeg = i == initIndex ? null : tree.getWhereSegments().get(i-1);
                if (prevSeg == null) {
                    // 前一个对象不存在，则说明当前对象是开始对象，跳出循环
                    break;
                }
                // ② 前一个对象是否有 rawExpr 即 SQL 片段
                if (StrUtil.isBlank(prevSeg.getSql())) {
                    seg.withLogicalOp(null);
                }
            }

            boolean needAddBracket = currentIndex-initIndex > 1;
            // 处理开始的逻辑运算符和开始括号
            tree.getWhereSegments().get(initIndex).withLogicalOp(LogicalOp.OR).withBeginBracket(needAddBracket);
            // 处理结束括号和结束逻辑运算符
            tree.getWhereSegments().get(tree.getWhereSegments().size()-1).withEndBracket(needAddBracket);
        }
        return self();
    }

    /** 子查询专用：返回 SelectSegment，可嵌入 SELECT/WHERE/IN 子句 */
    public SelectSegment toSelectSegment() {
        return () -> getSql();          // 参数已落库在 child
    }

    /* ========== 渲染出口 ========== */
    public String getSql() {
        /* 1. 渲染 SQL（已含 :name） */
        String sql = new JsqlParserDriver(this.dialect).toSql(this.tree, false);
        return renderSql(sql);
    }

    protected String renderSql(String sql){
        try {
            /* 2. 参数收集 & 合并（含子查询） */
            mergeRuleParams();

            /* 3. 按指定 SqlModel 替换占位符 */
            ParamBank bank = this.getContext().getParamBank();
            LinkedHashMap<String, Object> origenalMap = bank.toMap();
            ParamRenderStrategy renderer = createRenderer(origenalMap, this.sqlModel);
            this.paramMap = origenalMap;
            return renderer.render(sql);
        } finally {
            this.context.close(); // ✅ 清理 ParamBank 分支
        }
    }

    /** 渲染前：把 RuleContext 里的参数全部合并到 ParamBank */
    private void mergeRuleParams() {
        Map<String, Object> ruleMap = tree.getRuleContext().getParamMap();
        if (ruleMap.isEmpty()) return;

        ParamBank bank = this.getContext().getParamBank().current();
        SqlModel  model = this.sqlModel;          // 当前输出模式
        ruleMap.forEach((k, v) -> {
            // 如果 key 已经是 :name 形式，直接落库；否则生成新序号
            if (k.startsWith(ParamBank.getPrefix())) {
                bank.put(k, v);          // 复用原 key
            } if(!bank.containsKey(k)){
                bank.put(k, v);          // 复用原 key
            }else {
                String newKey = bank.nextParam(v); // 原子序号 + 落库
                // 把 RuleContext 里的 key 映射到新 key（防止插件重复塞）
//                ruleMap.put(k, ":" + model.render(newKey));
                ruleMap.put(k, ":" + newKey);
            }
        });
    }

    /** 渲染器工厂 */
    private ParamRenderStrategy createRenderer(LinkedHashMap<String, Object> map, SqlModel model) {
        return switch (model) {
            case PROPERTY_NAME -> new ColonNamedRender(map);   // 直接返回 :name
            case NATIVE        -> new QuestionRender(map);     // 转 ?
            case MYBATIS       -> new MybatisHashRender(map);  // 转 #{name}
            case NUMBER        -> new DollarNumberRender(map); // 转 $1
            case DIRECT_SQL    -> new DirectRender(map); // 直接将SQL和参数拼接到SQL中
            case CUSTOM_0      -> new Custom0(map);
            default            -> new ColonNamedRender(map);
        };
    }

    public LinkedHashMap<String, Object> getParamMap(){
        if(MapUtil.isEmpty(this.paramMap)){
            this.paramMap = this.getContext().getParamBank().toMap();
        }
        return this.paramMap;
    }

    /********************************** with语法 ***********************************/
    // 统一入口，不管 SELECT/INSERT/UPDATE/DELETE
    private String renderWith() {
        if (!dialect.supportsWith() || tree.getWithList().isEmpty()) {
            return "";                 // 不支持或没写 WITH 直接返回空串
        }
        return "WITH " +
                tree.getWithList().stream()
                        .map(WithSegment::getSql)
                        .collect(Collectors.joining(", ")) + " ";
    }
//    // 如果你的数据库支持 RECURSIVE，再做一个小扩展
//    public W withRecursive(String cteName, Consumer<LambdaQueryWrapper<?>> anchor,
//                           Consumer<LambdaQueryWrapper<?>> recursive, String... columns) {
//        LambdaQueryWrapper<?> a = new LambdaQueryWrapper<>(Object.class);
//        LambdaQueryWrapper<?> r = new LambdaQueryWrapper<>(Object.class);
//        anchor.accept(a);
//        recursive.accept(r);
//        SqlSegment union = UnionSegment.unionAll(a.tree, r.tree);
//        tree.with(WithSegment.as(cteName, union, columns).recursive());
//        return self();
//    }

    public W with(String cteName, Consumer<LambdaQueryWrapper<T>> cteBuilder, String... columns) {
        String subMainTableAlias = "sub"+StrUtil.upperFirst(this.getContext().getAliasRegistry().nextAlias());
        LambdaQueryWrapper<T> sub = new LambdaQueryWrapper<>(this.clazz, subMainTableAlias);
        sub.getContext().setParamBank(this.getContext().getParamBank());
        cteBuilder.accept(sub);
        tree.with(WithSegment.as(cteName, sub, columns));
        return self();
    }

    public W with(String cteName, AbstractWrapper<?, ?> withSqlgment) {
        SubQuerySegment subQuerySegment = new SubQuerySegment(withSqlgment, this);
        tree.with(WithSegment.as(cteName, subQuerySegment));
        return self();
    }

    public <D> W with(String cteName, SqlSegment withSqlgment) {
        tree.with(WithSegment.as(cteName, withSqlgment));
        return self();
    }

    /**  设置with查询的tableName **/
    public W from(String withTableName) {
        tree.withFromTable(withTableName);
        return self();
    }
}